import { NIM } from './index'

interface IThesaurus {
  // 1 关键词匹配 2 正则匹配
  match: number
  name: string
  /**
   * 匹配后的替换规则
   * 1 替换
   * 2 拒绝发送
   * 3 服务器处理（命中客户端反垃圾，交给服务器处理）,消息Property需要带上TalkMsgTag.clientAntiSpam
   */
  operate?: number
  /**
   * 额，不知道原来咋定义的。里面和外面的字段重合了很多
   */
  keys: Array<{
    operate: number
    match: number
    key: string
  }>
}

/**
 * 客户端反垃圾检查结果
 */
interface V2NIMClientAntispamResult {
  /**
   * 客户端反垃圾文本命中后操作类型
   */
  operateType: V2NIMClientAntispamOperateType
  /**
   * 替换后的文本
   */
  replacedText: string
}

const enum V2NIMClientAntispamOperateType {
  /** 无操作 */
  V2NIM_CLIENT_ANTISPAM_OPERATE_NONE = 0,
  /** 命中后，本地替换 */
  V2NIM_CLIENT_ANTISPAM_OPERATE_REPLACE = 1,
  /** 命中后，本地屏蔽，拒绝发送此消息 */
  V2NIM_CLIENT_ANTISPAM_OPERATE_CLIENT_SHIELD = 2,
  /** 命中后，消息可以发送，服务器屏蔽，即不会转发给其它用户 */
  V2NIM_CLIENT_ANTISPAM_OPERATE_SERVER_SHIELD = 3
}

interface V2NIMClientAntispamUtil {
  checkTextAntispam(text: string, replace: string): V2NIMClientAntispamResult
}

export default class V2NIMClientAntispamUtilImpl implements V2NIMClientAntispamUtil {
  core: NIM
  vocabInfo:
    | undefined
    | {
        version: string
        md5: string
        nosurl: string
        thesaurus: Array<IThesaurus>
      }
  name: string

  constructor(core: NIM) {
    this.name = 'V2NIMClientAntispamUtil'
    this.core = core
  }

  /**
   * 本地客户端反垃圾检测
   */
  checkTextAntispam(text: string, replace = '**'): V2NIMClientAntispamResult {
    if (!this.vocabInfo) {
      return {
        operateType: V2NIMClientAntispamOperateType.V2NIM_CLIENT_ANTISPAM_OPERATE_NONE,
        replacedText: text
      }
    }

    let newText = text
    for (let i = 0; i < this.vocabInfo.thesaurus.length; i++) {
      const res = this.filterContent(newText, this.vocabInfo.thesaurus[i], replace)
      /**
       * 多段替换时，下一段检测需要基于上一段替换后的文本
       */
      newText = res.replacedText

      /**
       * 客户端或者服务端屏蔽
       */
      if (
        res.operateType === V2NIMClientAntispamOperateType.V2NIM_CLIENT_ANTISPAM_OPERATE_CLIENT_SHIELD ||
        res.operateType === V2NIMClientAntispamOperateType.V2NIM_CLIENT_ANTISPAM_OPERATE_SERVER_SHIELD
      ) {
        return res
      }
    }

    return {
      operateType:
        newText === text
          ? V2NIMClientAntispamOperateType.V2NIM_CLIENT_ANTISPAM_OPERATE_NONE
          : V2NIMClientAntispamOperateType.V2NIM_CLIENT_ANTISPAM_OPERATE_REPLACE,
      replacedText: newText
    }
  }

  /**
   * 使用 thesaurus 中单个规则进行过滤
   */
  private filterContent(text: string, thesaurus: IThesaurus, replace: string): V2NIMClientAntispamResult {
    for (let i = 0; i < thesaurus.keys.length; i++) {
      const item = thesaurus.keys[i]
      const matchType = item.match || thesaurus.match
      const operateType = (item.operate || thesaurus.operate) as number
      let res
      try {
        res = this.matchContent(text, item.key, matchType, operateType, replace)
      } catch (error) {
        // 有些正则表达式web不支持
        // this.logger.warn('V2NIMLocalAntispamUtil::filterContent matchContent error', error)
      }

      if (!res) continue

      text = res.replacedText
      /**
       * 客户端或者服务端屏蔽
       */
      if (
        res.operateType === V2NIMClientAntispamOperateType.V2NIM_CLIENT_ANTISPAM_OPERATE_CLIENT_SHIELD ||
        res.operateType === V2NIMClientAntispamOperateType.V2NIM_CLIENT_ANTISPAM_OPERATE_SERVER_SHIELD
      ) {
        return res
      }
    }

    /**
     * 无论是否替换了文本，都可以使用 V2NIM_CLIENT_ANTISPAM_OPERATE_REPLACE 返回。最终是否替换的判定在 checkTextAntispam 的返回结果中
     */
    return {
      operateType: V2NIMClientAntispamOperateType.V2NIM_CLIENT_ANTISPAM_OPERATE_REPLACE,
      replacedText: text
    }
  }

  /**
   * 实际检测函数
   */
  private matchContent(text: string, keyword: string, matchType: number, operateType: number, replace: string): V2NIMClientAntispamResult {
    let isMatched = false
    let reg: RegExp | null = null
    if (matchType === 1) {
      // 1: 关键词匹配
      if (text.indexOf(keyword) >= 0) {
        isMatched = true
        // OMIM-60272 修复问题: 本地反垃圾匹配的文本匹配规则只能替换一个单词
        // 因为 replaceAll 是 ES2021 才引入的, 所以这里使用 replaceAll 的替代方案.
        // 防止过滤用的 keyword 自身有什么特殊字符, 以下代码由 DeepSeek 生成.
        const escapedSearch = keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
        reg = new RegExp(escapedSearch, 'g')
      }
    } else if (matchType === 2) {
      // 2: 正则表达式匹配
      reg = new RegExp(keyword, 'g')
      if (reg.test(text)) {
        isMatched = true
      }
    }
    if (isMatched && reg !== null) {
      switch (operateType) {
        // 1：替换
        case 1:
          return {
            operateType: V2NIMClientAntispamOperateType.V2NIM_CLIENT_ANTISPAM_OPERATE_REPLACE,
            replacedText: text.replace(reg, replace)
          }
        // 2：拒绝发送
        case 2:
          return {
            operateType: V2NIMClientAntispamOperateType.V2NIM_CLIENT_ANTISPAM_OPERATE_CLIENT_SHIELD,
            replacedText: text
          }
        // 3：服务器处理（命中客户端反垃圾，交给服务器处理）,消息Property需要带上TalkMsgTag.clientAntiSpam
        case 3:
          return {
            operateType: V2NIMClientAntispamOperateType.V2NIM_CLIENT_ANTISPAM_OPERATE_SERVER_SHIELD,
            replacedText: text
          }
      }
    }
    return {
      operateType: V2NIMClientAntispamOperateType.V2NIM_CLIENT_ANTISPAM_OPERATE_NONE,
      replacedText: text
    }
  }
}
